home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 2: CDPD 1 / Almathera Ten on Ten - Disc 2: CDPD 1.iso / pd / 051-075 / 074 / less / command.c < prev    next >
C/C++ Source or Header  |  1995-03-13  |  10KB  |  624 lines

  1. /*
  2.  * User-level command processor.
  3.  */
  4.  
  5. #include "less.h"
  6. #include "position.h"
  7. #include <setjmp.h>
  8.  
  9. extern jmp_buf main_loop;
  10. extern int erase_char, kill_char;
  11. extern int pr_type;
  12. extern int sigs;
  13. extern int ispipe;
  14. extern int quit_at_eof;
  15. extern int hit_eof;
  16. extern int sc_width, sc_height;
  17. extern char *first_cmd;
  18. extern char version[];
  19. extern char current_file[];
  20. extern char *editor;
  21.  
  22. #ifdef amiga
  23. extern ac, curr_ac;        /* local argc for file names */
  24. int user_errors = 0;
  25. #endif
  26.  
  27. static char cmdbuf[90];         /* Buffer for holding a multi-char command */
  28. static char *cp;                /* Pointer into cmdbuf */
  29. static int cmd_col;             /* Current column of the multi-char command */
  30. static char mcc;                /* The multi-char command letter (e.g. '/') */
  31. static char last_mcc;           /* The previous mcc */
  32.  
  33. /*
  34.  * Reset command buffer (to empty).
  35.  */
  36. cmd_reset()
  37. {
  38.     cp = cmdbuf;
  39. }
  40.  
  41. /*
  42.  * Backspace in command buffer.
  43.  */
  44.     static int
  45. cmd_erase()
  46. {
  47.     if (cp == cmdbuf)
  48.         /*
  49.          * Backspace past beginning of the string:
  50.          * this usually means abort the command.
  51.          */
  52.         return (1);
  53.  
  54.     if (control_char(*--cp))
  55.     {
  56.         /*
  57.          * Erase an extra character, for the carat.
  58.          */
  59.         backspace();
  60.         cmd_col--;
  61.     }
  62.     backspace();
  63.     cmd_col--;
  64.     return (0);
  65. }
  66.  
  67. /*
  68.  * Set up the display to start a new multi-character command.
  69.  */
  70. start_mcc()
  71. {
  72.     lower_left();
  73.     clear_eol();
  74.     putc(mcc);
  75.     cmd_col = 1;
  76. }
  77.  
  78. /*
  79.  * Process a single character of a multi-character command, such as
  80.  * a number, or the pattern of a search command.
  81.  */
  82.     static int
  83. cmd_char(c)
  84.     int c;
  85. {
  86.     if (c == erase_char)
  87.     {
  88.         if (cmd_erase())
  89.             return (1);
  90.     } else if (c == kill_char)
  91.     {
  92.         /* {{ Could do this faster, but who cares? }} */
  93.         while (cmd_erase() == 0)
  94.             ;
  95.     } else
  96.     {
  97.         /*
  98.          * Append the character to the string,
  99.          * if there is room in the buffer and on the screen.
  100.          */
  101.         if (cp < &cmdbuf[sizeof(cmdbuf)-1] && cmd_col < sc_width-3)
  102.         {
  103.             *cp++ = c;
  104.             if (control_char(c))
  105.             {
  106.                 putc('^');
  107.                 cmd_col++;
  108.                 c = carat_char(c);
  109.             }
  110.             putc(c);
  111.             cmd_col++;
  112.         } else
  113.             bell();
  114.     }
  115.     return (0);
  116. }
  117.  
  118. /*
  119.  * Return the number currently in the command buffer.
  120.  */
  121.     static int
  122. cmd_int()
  123. {
  124.     *cp = '\0';
  125.     cp = cmdbuf;
  126.     return (atoi(cmdbuf));
  127. }
  128.  
  129. /*
  130.  * Move the cursor to lower left before executing a command.
  131.  * This looks nicer if the command takes a long time before
  132.  * updating the screen.
  133.  */
  134.     static void
  135. cmd_exec()
  136. {
  137.     lower_left();
  138.     flush();
  139. }
  140.  
  141. /*
  142.  * Display the appropriate prompt.
  143.  */
  144.     static void
  145. prompt()
  146. {
  147.     register char *p;
  148.  
  149.     if (first_cmd != NULL && *first_cmd != '\0')
  150.         /*
  151.          * No prompt necessary if commands are from first_cmd
  152.          * rather than from the user.
  153.          */
  154.         return;
  155.  
  156.     /*
  157.      * Select the proper prompt and display it.
  158.      */
  159.     p = pr_string();
  160.     if (p == NULL)
  161.         putc(':');
  162.     else
  163.     {
  164.         so_enter();
  165.         puts(p);
  166.         so_exit();
  167.     }
  168. }
  169.  
  170. /*
  171.  * Get command character.
  172.  * The character normally comes from the keyboard,
  173.  * but may come from the "first_cmd" string.
  174.  */
  175.     static int
  176. getcc()
  177. {
  178.     if (first_cmd == NULL)
  179.         return (getc());
  180.  
  181.     if (*first_cmd == '\0')
  182.     {
  183.         /*
  184.          * Reached end of first_cmd input.
  185.          */
  186.         first_cmd = NULL;
  187.         if (cp > cmdbuf && position(TOP) == NULL_POSITION)
  188.         {
  189.             /*
  190.              * Command is incomplete, so try to complete it.
  191.              * There are only two cases:
  192.              * 1. We have "/string" but no newline.  Add the \n.
  193.              * 2. We have a number but no command.  Treat as #g.
  194.              * (This is all pretty hokey.)
  195.              */
  196.             if (mcc != ':')
  197.                 return ('\n'); 
  198.             else
  199.                 return ('g');
  200.         }
  201.         return (getc());
  202.     }
  203.     return (*first_cmd++);
  204. }
  205.  
  206. /*
  207.  * Main command processor.
  208.  * Accept and execute commands until a quit command, then return.
  209.  */
  210.     public void
  211. commands()
  212. {
  213.     register int c;
  214.     register int n;
  215.     register int scroll = 10;
  216.  
  217.     mcc = last_mcc = 0;
  218.  
  219.     setjmp(main_loop);
  220.     for (;;)
  221.     {
  222.         /*
  223.          * Display prompt and accept a character.
  224.          */
  225.         psignals();     /* See if any signals need processing */
  226.  
  227.         if (quit_at_eof && hit_eof > 1)
  228.             /*
  229.              * After hitting end-of-file for the second time,
  230.              * automatically advance to the next file.
  231.              * If there are no more files, quit.
  232.              */
  233.             next_file(1);
  234.  
  235.         cmd_reset();
  236.         lower_left();
  237.         clear_eol();
  238.         prompt();
  239.         c = getcc();
  240.  
  241.     again:
  242.         if (sigs)
  243.             continue;
  244.  
  245.         if (mcc)
  246.         {
  247.             /*
  248.              * We are in a multi-character command.  
  249.              * All chars until newline go into the command buffer.
  250.              * (Note that mcc == ':' is a special case that
  251.              *  means a number is being entered.)
  252.              */
  253.             if (mcc != ':' && (c == '\n' || c == '\r'))
  254.             {
  255.                 /*
  256.                  * Execute the command.
  257.                  */
  258.                 *cp = '\0';
  259.                 cmd_exec();
  260.                 if (mcc == 'E')
  261.                 {
  262.                     char *p;
  263.                     /*
  264.                      * Ignore leading spaces 
  265.                      * in the filename.
  266.                      */
  267.                     for (p = cmdbuf;  *p == ' ';  p++) ;
  268.                     edit(p);
  269. #if SHELL_ESCAPE
  270.                 } else if (mcc == '!')
  271.                 {
  272.                     lsystem(cmdbuf);
  273.                     error("!done");
  274.                     first_cmd = "r";        /* Repaint */
  275. #endif
  276.                 } else
  277.                     search(mcc, cmdbuf, n);
  278.                 mcc = 0;
  279.             } else
  280.             {
  281.                 if (mcc == ':' && (c < '0' || c > '9') &&
  282.                     c != erase_char && c != kill_char)
  283.                 {
  284.                     /*
  285.                      * This is not part of the number
  286.                      * we were entering.  Process
  287.                      * it as a regular character.
  288.                      */
  289.                     mcc = 0;
  290.                     goto again;
  291.                 }
  292.  
  293.                 /*
  294.                  * Append the char to the command buffer.
  295.                  */
  296.                 if (cmd_char(c))
  297.                 {
  298.                     /* Abort the multi-char command. */
  299.                     mcc = 0;
  300.                     continue;
  301.                 }
  302.                 c = getcc();
  303.                 goto again;
  304.             }
  305.         } else switch (c)
  306.         {
  307.         case '0': case '1': case '2': case '3': case '4':
  308.         case '5': case '6': case '7': case '8': case '9':
  309.             /*
  310.              * First digit of a number.
  311.              */
  312.             mcc = ':';
  313.             start_mcc();
  314.             goto again;
  315.  
  316.         case 'f':
  317.         case ' ':
  318.         case CONTROL('F'):
  319.             /*
  320.              * Forward one screen.
  321.              */
  322.     /* not needed        if (hit_eof) {
  323.                 if (curr_ac+1 == ac)
  324.                     return;
  325.                 else
  326.                     next_file(1);
  327.             } else*/ {
  328.                 n = cmd_int();
  329.                 if (n <= 0)
  330.                     n = sc_height - 1;
  331.                 forward(n, 1);
  332.             }
  333.             break;
  334.  
  335.         case 'b':
  336.         case 'B':
  337.         case CONTROL('B'):
  338.             /*
  339.              * Backward one screen.
  340.              */
  341.             n = cmd_int();
  342.             if (n <= 0)
  343.                 n = sc_height - 1;
  344.             backward(n, 1);
  345.             break;
  346.  
  347.         case 'e':
  348.         case 'j':
  349.         case '\r':
  350.         case '\n':
  351.         case CONTROL('E'):
  352.             /*
  353.              * Forward N (default 1) line.
  354.              */
  355.             n = cmd_int();
  356.             if (n <= 0)
  357.                 n = 1;
  358.             forward(n, 0);
  359.             break;
  360.  
  361.         case 'y':
  362.         case 'k':
  363.         case CONTROL('K'):
  364.         case CONTROL('Y'):
  365.             /*
  366.              * Backward N (default 1) line.
  367.              */
  368.             n = cmd_int();
  369.             if (n <= 0)
  370.                 n = 1;
  371.             backward(n, 0);
  372.             break;
  373.  
  374.         case 'd':
  375.         case CONTROL('D'):
  376.             /*
  377.              * Forward N lines 
  378.              * (default same as last 'd' or 'u' command).
  379.              */
  380.             n = cmd_int();
  381.             if (n > 0)
  382.                 scroll = n;
  383.             forward(scroll, 0);
  384.             break;
  385.  
  386.         case 'u':
  387.         case CONTROL('U'):
  388.             /*
  389.              * Forward N lines 
  390.              * (default same as last 'd' or 'u' command).
  391.              */
  392.             n = cmd_int();
  393.             if (n > 0)
  394.                 scroll = n;
  395.             backward(scroll, 0);
  396.             break;
  397.  
  398.         case 'R':
  399.             /*
  400.              * Flush buffers, then repaint screen.
  401.              */
  402.             ch_init(0);
  403.             /* Fall thru */
  404.         case 'r':
  405.         case CONTROL('R'):
  406.         case CONTROL('L'):
  407.             /*
  408.              * Repaint screen.
  409.              */
  410.             repaint();
  411.             break;
  412.  
  413.         case 'g':
  414.             /*
  415.              * Go to line N, default beginning of file.
  416.              */
  417.             n = cmd_int();
  418.             if (n <= 0)
  419.                 n = 1;
  420.             cmd_exec();
  421.             jump_back(n);
  422.             break;
  423.  
  424.         case 'p':
  425.         case '%':
  426.             /*
  427.              * Go to a specified percentage into the file.
  428.              */
  429.             n = cmd_int();
  430.             if (n < 0)
  431.                 n = 0;
  432.             if (n > 100)
  433.                 n = 100;
  434.             cmd_exec();
  435.             jump_percent(n);
  436.             break;
  437.  
  438.         case 'G':
  439.             /*
  440.              * Go to line N, default end of file.
  441.              */
  442.             n = cmd_int();
  443.             cmd_exec();
  444.             if (n <= 0)
  445.                 jump_forw();
  446.             else
  447.                 jump_back(n);
  448.             break;
  449.  
  450.         case '=':
  451.         case CONTROL('G'):
  452.             /*
  453.              * Print file name, etc.
  454.              */
  455.             error(eq_message());
  456.             break;
  457.             
  458.         case 'V':
  459.             /*
  460.              * Print version number, without the "@(#)".
  461.              */
  462.             error(version+4);
  463.             break;
  464.  
  465.         case 'q':
  466.         case 'Q':
  467.             /*
  468.              * Exit.
  469.              */
  470.             return;
  471.  
  472.         case '/':
  473.         case '?':
  474.             /*
  475.              * Search for a pattern.
  476.              * Accept chars of the pattern until \n.
  477.              */
  478.             n = cmd_int();
  479.             if (n <= 0)
  480.                 n = 1;
  481.             mcc = last_mcc = c;
  482.             start_mcc();
  483.             c = getcc();
  484.             goto again;
  485.  
  486.         case 'n':
  487.             /*
  488.              * Repeat previous search.
  489.              */
  490.             n = cmd_int();
  491.             if (n <= 0)
  492.                 n = 1;
  493.             mcc = last_mcc;
  494.             start_mcc();
  495.             cmd_exec();
  496.             search(mcc, (char *)NULL, n);
  497.             mcc = 0;
  498.             break;
  499.  
  500.         case 'h':
  501.         case 'H':
  502.             /*
  503.              * Help.
  504.              */
  505.             help();
  506.             repaint();
  507.             break;
  508.  
  509.         case 'E':
  510.             /*
  511.              * Examine a new file.  Get the filename.
  512.              */
  513.             cmd_reset();
  514.             mcc = 'E';
  515.             start_mcc();
  516.             puts("xamine: ");  /* This looks nicer */
  517.             cmd_col += 8;
  518.             c = getcc();
  519.             goto again;
  520.             
  521. #if SHELL_ESCAPE
  522.         case '!':
  523.             /*
  524.              * Shell escape.
  525.              */
  526.             cmd_reset();
  527.             mcc = '!';
  528.             start_mcc();
  529.             c = getcc();
  530.             goto again;
  531. #endif
  532.  
  533. #if EDITOR
  534.         case 'v':
  535.             if (ispipe)
  536.             {
  537.                 error("Cannot edit standard input");
  538.                 break;
  539.             }
  540.             sprintf(cmdbuf, "%s %s", editor, current_file);
  541.             lsystem(cmdbuf);
  542.             first_cmd = "R";
  543.             break;
  544. #endif
  545.  
  546.         case 'N':
  547.             /*
  548.              * Examine next file.
  549.              */
  550.             n = cmd_int();
  551.             if (n <= 0)
  552.                 n = 1;
  553.             next_file(n);
  554.             break;
  555.  
  556.         case 'P':
  557.             /*
  558.              * Examine previous file.
  559.              */
  560.             n = cmd_int();
  561.             if (n <= 0)
  562.                 n = 1;
  563.             prev_file(n);
  564.             break;
  565.  
  566.         case '-':
  567.             /*
  568.              * Toggle a flag setting.
  569.              */
  570.             mcc = '-';
  571.             start_mcc();
  572.             c = getcc();
  573.             mcc = 0;
  574.             if (c == erase_char || c == kill_char)
  575.                 break;
  576.             toggle_option(c);
  577.             break;
  578.  
  579.         case 'm':
  580.             /*
  581.              * Set a mark.
  582.              */
  583.             lower_left();
  584.             clear_eol();
  585.             puts("mark: ");
  586.             c = getcc();
  587.             if (c == erase_char || c == kill_char)
  588.                 break;
  589.             setmark(c);
  590.             break;
  591.  
  592.         case '\'':
  593.             /*
  594.              * Go to a mark.
  595.              */
  596.             lower_left();
  597.             clear_eol();
  598.             puts("goto mark: ");
  599.             c = getcc();
  600.             if (c == erase_char || c == kill_char)
  601.                 break;
  602.             gomark(c);
  603.             break;
  604.  
  605.         default:
  606. #ifdef amiga
  607.         if (++user_errors > 2) {
  608.            lower_left();
  609.            clear_eol();
  610.            so_enter();
  611.            putc(c);
  612.            puts(" is an Invalid Command, Type H for help, or Q to quit");
  613.            so_exit();
  614.            /* give him some time to read it, and three more trys */
  615.            Delay(3 * 50L);
  616.            user_errors = 0;
  617.         } else
  618. #endif
  619.             bell();
  620.             break;
  621.         }
  622.     }
  623. }
  624.